home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™ 1987-1994 / MacHack™ '90 / MacHack'90 Proceedings / John Norstad / Reusable Code / Source / vmsg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-10  |  18.1 KB  |  680 lines  |  [TEXT/MPS ]

  1. /*______________________________________________________________________
  2.  
  3.     vmsg.c - Virus Message Module.
  4.     
  5.     Copyright © 1988, 1989, 1990, Northwestern University.
  6.     
  7.     ***** IMPORTANT *****.  This is a private module.  Please don't
  8.     distribute it to the public.
  9.     
  10.     This module contains the scan begin and end routines, and a variety
  11.     of helper routines for issuing messages to the report.  It also
  12.     maintains the three counters in the main window.
  13. _____________________________________________________________________*/
  14.  
  15.  
  16. #pragma load "precompile"
  17. #include "utl.h"
  18. #include "rep.h"
  19. #include "vol.h"
  20. #include "rez.h"
  21. #include "glob.h"
  22. #include "vmsg.h"
  23.  
  24. #pragma segment vmsg
  25.  
  26.  
  27. /*______________________________________________________________________
  28.  
  29.     Global Types and Variables.
  30. _____________________________________________________________________*/
  31.  
  32.  
  33. typedef struct TagTableEntry {
  34.     short        reportLineNum;
  35.     short        tag;
  36. } TagTableEntry;
  37.  
  38.  
  39. static TagTableEntry        (**TagTable)[] = nil;    /* handle to tag table */
  40. static short                NTags = 0;                    /* number of tags */
  41. static short                NAlloc = 0;                    /* number of allocated
  42.                                                                     tag table entries */
  43. static short                CounterTop;                    /* top coord of counters */
  44. static short                CounterRight;                /* right coord of counters */
  45. static Str255                Line1;                        /* unplugged message line */
  46. static Str255                Line2;                        /* plugged message line */
  47.  
  48.  
  49. /*______________________________________________________________________
  50.  
  51.     RecordTag - Record a New Tag.
  52.     
  53.     Entry:    tag = tag to be recorded.  If the tag is zero or if it
  54.                 does not appear in the TAG resource it is not
  55.                 recorded.
  56.                     
  57.     Exit:        current report line number and corresponding tag
  58.                 recorded in table.
  59. _____________________________________________________________________*/
  60.  
  61.  
  62. static void RecordTag (short tag)
  63.  
  64. {
  65.     TagTableEntry        tagEntry;    /* new entry for tag table */
  66.     
  67.     if (!tag || !rep_Tag(tagID, tag)) return;
  68.     tagEntry.reportLineNum = rep_GetSize(Report);
  69.     tagEntry.tag = tag;
  70.     
  71.     /* Allocate more space in tag table if necessary. */
  72.     
  73.     if (NTags >= NAlloc) {
  74.         if (!TagTable) {
  75.             TagTable = (TagTableEntry(**)[])NewHandle(10*sizeof(TagTableEntry));
  76.             NAlloc = 10;
  77.         } else {
  78.             SetHandleSize((Handle)TagTable, 
  79.                 GetHandleSize((Handle)TagTable) + 10*sizeof(TagTableEntry));
  80.             NAlloc += 10;
  81.         };
  82.     };
  83.     
  84.     /* Record new tag table entry. */
  85.     
  86.     (**TagTable)[NTags] = tagEntry;
  87.     NTags++;
  88. }
  89.  
  90. /*______________________________________________________________________
  91.  
  92.     vmsg_LookupTag - Lookup a Tag.
  93.     
  94.     Entry:    repLine = line number in report of error message.
  95.                     
  96.     Exit:        function result = tag of doc text of error
  97.                 message description, or -1 if the report line number is
  98.                 not an error message.
  99. _____________________________________________________________________*/
  100.  
  101.  
  102. short vmsg_LookupTag (short repLine)
  103.  
  104. {
  105.     short            i;
  106.     
  107.     for (i = 0; i < NTags; i++) {
  108.         if ((**TagTable)[i].reportLineNum == repLine) 
  109.             return (**TagTable)[i].tag;
  110.     };
  111.     return -1;
  112. }
  113.  
  114. /*______________________________________________________________________
  115.  
  116.     vmsg_ClearTags - Clear the Tag Table.
  117.     
  118.     This routine should be called whenever the report is cleared.
  119. _____________________________________________________________________*/
  120.  
  121.  
  122. void vmsg_ClearTags (void)
  123.  
  124. {
  125.     if (TagTable) {
  126.         DisposHandle((Handle)TagTable);
  127.         TagTable = nil;
  128.     };
  129.     NTags = NAlloc = 0;
  130. }
  131.  
  132. /*______________________________________________________________________
  133.  
  134.     vmsg_M0 - Issue a Report Message with 0 Parameters.
  135.     vmsg_M1 - Issue a Report Message with 1 Parameter.
  136.     vmsg_M2 - Issue a Report Message with 2 Parameters.
  137.     vmsg_M2 - Issue a Report Message with 2 Parameters and a tag.
  138.     vmsg_Continue - Issue a Report Message Continuation Line.
  139.     vmsg_Blank - Issue a Blank Line to the Report.
  140.     
  141.     Entry:        strInd = index in STR# of message template.
  142.                     p0 = pointer to param 1.
  143.                     p1 = pointer to param 2.
  144.                     msgNum = message number = index in STR# of first line
  145.                         of error message.
  146.                     tag = document tag.
  147. _____________________________________________________________________*/
  148.  
  149.  
  150. void vmsg_M0 (short strInd)
  151.  
  152. {
  153.     GetIndString(Line1, strListID, strInd);
  154.     RecordTag(strInd);
  155.     rep_Append(Report, Line1, true, true);
  156. }
  157.  
  158.  
  159. void vmsg_M1 (short strInd, Str255 p0)
  160.  
  161. {
  162.     GetIndString(Line1, strListID, strInd);
  163.     utl_PlugParams(Line1, Line2, p0, nil, nil, nil);
  164.     RecordTag(strInd);
  165.     rep_Append(Report, Line2, true, true);
  166. }
  167.  
  168.  
  169. void vmsg_M2 (short strInd, Str255 p0, Str255 p1)
  170.  
  171. {
  172.     GetIndString(Line1, strListID, strInd);
  173.     utl_PlugParams(Line1, Line2, p0, p1, nil, nil);
  174.     RecordTag(strInd);
  175.     rep_Append(Report, Line2, true, true);
  176. }
  177.  
  178.  
  179. void vmsg_M2_Tag (short strInd, Str255 p0, Str255 p1, short tag)
  180.  
  181. {
  182.     GetIndString(Line1, strListID, strInd);
  183.     utl_PlugParams(Line1, Line2, p0, p1, nil, nil);
  184.     RecordTag(tag);
  185.     rep_Append(Report, Line2, true, true);
  186. }
  187.  
  188.  
  189. void vmsg_Continue (short strInd, short msgNum)
  190.  
  191. {
  192.     GetIndString(Line1, strListID, strInd);
  193.     RecordTag(msgNum);
  194.     rep_Append(Report, Line1, true, true);
  195. }
  196.  
  197.  
  198. void vmsg_Blank (void)
  199. {
  200.     rep_Append(Report, "\p", true, true);
  201. }
  202.  
  203. /*______________________________________________________________________
  204.  
  205.     vmsg_MInf - Issue File Infected Error Message.
  206.     
  207.     Entry:    vName = pointer to name of virus.
  208.                 tag = tag for document section on this virus.
  209. _____________________________________________________________________*/
  210.  
  211.  
  212. void vmsg_MInf (Str255 vName, short tag)
  213.  
  214. {
  215.     GetIndString(Line1, strListID, infectedStr);
  216.     utl_PlugParams(Line1, Line2, vName, nil, nil, nil);
  217.     RecordTag(tag);
  218.     rep_Append(Report, Line2, true, true);
  219. }
  220.  
  221. /*______________________________________________________________________
  222.  
  223.     vmsg_Unexpected - Issue Unexpected Error Message.
  224.     
  225.     Entry:    rCode = error code.
  226. _____________________________________________________________________*/
  227.  
  228.  
  229. void vmsg_Unexpected (OSErr rCode)
  230.  
  231. {
  232.     Str255        decNum;            /* error code converted to a string */
  233.     
  234.     NumToString(rCode, decNum);
  235.     vmsg_M1(unexpectedStr, decNum);
  236. };
  237.  
  238. /*______________________________________________________________________
  239.  
  240.     PrintPath - Print Indented Path Name.
  241.     
  242.     Entry:    folderList = handle to folder list.
  243.                     
  244.     Exit:        level = indentation level for next element of path.
  245.                 tag = document tag to be associated with each line of the
  246.                     path name list in the report, or 0 if none.
  247.     
  248.     This function calls itself recursively to output the folder list
  249.     in reverse order.
  250. _____________________________________________________________________*/
  251.  
  252.  
  253. static void PrintPath (scn_FListElHandle folderList, short *level, short tag)
  254.  
  255. {
  256.     short                myLevel;            /* my level */
  257.     char                *p;                /* pointer into Line1 */
  258.     short                len;                /* length of folder name */
  259.     
  260.     if (*(**folderList).name) {
  261.         PrintPath((**folderList).next, &myLevel, tag);
  262.         *level = myLevel + 3;
  263.         p = Line1+1;
  264.         while (myLevel--) *p++ = ' ';
  265.         len = *(**folderList).name;
  266.         if (p + len >= Line1 + 256) len = Line1 + 256 - p;
  267.         memcpy(p, (**folderList).name + 1, len);
  268.         *Line1 = p - Line1 - 1 + len;
  269.         RecordTag(tag);
  270.         rep_Append(Report, Line1, true, true);
  271.     } else {
  272.         *level = 0;
  273.     }
  274. }        
  275.  
  276. /*______________________________________________________________________
  277.  
  278.     vmsg_PrintFileName - Print the File Name.
  279.     
  280.     Entry:    folderList = handle to folder list.
  281.                 pBlock = pointer to PBGetCatInfo param block.
  282.                 tag = document tag to be associated with each line of the
  283.                     path name list in the report, or 0 if none.
  284. _____________________________________________________________________*/
  285.  
  286.  
  287. void vmsg_PrintFileName (scn_FListElHandle folderList, 
  288.     CInfoPBRec *pBlock, short tag)
  289.  
  290. {
  291.     short                    indentLevel;        /* indentation level */
  292.     char                    *pLine;                /* pointer into message line */
  293.     short                    fileNameLen;        /* length of file name */
  294.  
  295.     if (CurScanKind == fileScan) return;
  296.     rep_Append(Report, "\p--------------------------------------------", 
  297.         true, true);
  298.     PrintPath(folderList, &indentLevel, tag);
  299.     pLine = Line1+1;
  300.     while (indentLevel--) *pLine++ = ' ';
  301.     fileNameLen = *pBlock->hfileInfo.ioNamePtr;
  302.     if (pLine + fileNameLen >= Line1 + 256)
  303.         fileNameLen = Line1 + 256 - pLine;
  304.     memcpy(pLine, pBlock->hfileInfo.ioNamePtr + 1, fileNameLen);
  305.     *Line1 = pLine - Line1 - 1 + fileNameLen;
  306.     RecordTag(tag);
  307.     rep_Append(Report, Line1, true, true);
  308. }
  309.  
  310. /*______________________________________________________________________
  311.  
  312.     PrintSum - Print Summary Line.
  313.     
  314.     Entry:        theNum = longword summary counter.
  315.                     theMsg = index in STR# resource of plural form of message, 
  316.                         followed by singular form of message. 
  317.                     thePlug = pointer to ^1 plug, or nil if none.
  318. _____________________________________________________________________*/
  319.  
  320.  
  321. static void PrintSum (long theNum, short theMsg, Str255 thePlug)
  322.  
  323. {
  324.     Str255            decNum;            /* converted number */
  325.     
  326.     NumToString(theNum, decNum);
  327.     vmsg_M2(theMsg + ((theNum==1) ? 1 : 0), decNum, thePlug);
  328. }
  329.  
  330. /*______________________________________________________________________
  331.  
  332.     vmsg_CheckAccess - Check Directory Access Privileges.
  333.     
  334.     Entry:    accessRights = ioACUser field from PBGetCatInfo param
  335.                     block.  (see IM V-391).
  336.                 folderList = handle to folder list to be printed, or
  337.                     nil to inhibit printing of folder list.
  338.                 pBlock = pointer to PBGetCatInfo param block
  339.                     if folderList != nil.
  340. _____________________________________________________________________*/
  341.  
  342.  
  343. void vmsg_CheckAccess (char accessRights, 
  344.     scn_FListElHandle folderList, CInfoPBRec *pBlock)
  345.     
  346. {
  347.     short            firstMsg;            /* index of first message */
  348.  
  349.     /* Return if user has both See Files and See Folders access
  350.         privileges. */
  351.  
  352.     if (!(accessRights &= 3)) return;
  353.     
  354.     /* Print folder path if folderList != nil. */
  355.     
  356.     firstMsg = noPrivs1 + 3*(accessRights-1);
  357.     if (folderList) {
  358.         vmsg_PrintFileName(folderList, pBlock, firstMsg);
  359.     };
  360.     
  361.     /* Issue error message. */
  362.     
  363.     vmsg_M0(firstMsg);
  364.     vmsg_Continue(firstMsg+1, firstMsg);
  365.     vmsg_Continue(firstMsg+2, firstMsg);
  366.     TotErrors++;
  367.     vmsg_BumpCounter(2);
  368.     TotNoAccess++;
  369. }
  370.  
  371. /*______________________________________________________________________
  372.  
  373.     vmsg_Begin - Begin a Scan.
  374.     
  375.     Entry            disinfect = true if disinfection run.
  376.                     dirID = directory id of folder if folder scan.
  377.                     fName = pointer to file name if file scan.
  378.                     fVRefNum = vol or wd ref num of folder containing file
  379.                         if file scan.
  380.                     volRefNum = vol ref num of vol if volume scan, or vol ref
  381.                         num of vol containing file or folder if file or folder 
  382.                         scan.
  383.                     counterTop = top coord of main window counters.
  384.                     counterRight = right coord of main window counters.
  385. _____________________________________________________________________*/
  386.  
  387.  
  388. void vmsg_Begin (Boolean disinfect, long dirID, Str255 fName, 
  389.     short fVRefNum, short volRefNum, short counterTop, short counterRight)
  390.  
  391. {
  392.     unsigned long    secs;                    /* current date/time */
  393.     Str255            nowDate;                /* current date */
  394.     Str255            nowTime;                /* current time */
  395.     scn_FListElHandle    folderList;        /* handle to first el in folder list */
  396.     scn_FListElHandle    newEl;            /* handle to new el for folder list */
  397.     scn_FListElHandle    lastEl;            /* handle to last el in folder list */
  398.     WDPBRec            wdBlock;                /* working directory info block */
  399.     long                curDir;                /* directory id */
  400.     Str255            dirName;                /* directory name */
  401.     CInfoPBRec        dBlock;                /* directory info block */
  402.     short                level;                /* indentation level */
  403.     Boolean            firstDir;            /* true if first directory */
  404.     char                accessRights;        /* directory access rights */
  405.  
  406.     /* Initialize global variables. */
  407.     
  408.     TotFiles = TotErrors = TotInfected = TotNoAccess = 0;
  409.     EarliestDate = 0xffffffffL;
  410.     SysInfected = false;
  411.     CounterTop = counterTop;
  412.     CounterRight = counterRight;
  413.     
  414.     /* Write the report header lines. */
  415.     
  416.     vmsg_Blank();
  417.     rep_Append(Report, "\p===========================================",
  418.         true, true);
  419.     vmsg_Blank();
  420.     
  421.     /* Write the volume name, folder path list, or file path list.
  422.         For folders and files we build a temporary linked list of
  423.         folder names for use by PrintPath. */
  424.     
  425.     if (utl_VolIsMFS(volRefNum)) {
  426.         vol_GetName(Line1);
  427.         rep_Append(Report, Line1, true, true);
  428.         if (CurScanKind == fileScan) {
  429.             *Line1 = *fName+3;
  430.             *(Line1+1) = ' ';
  431.             *(Line1+2) = ' ';
  432.             *(Line1+3) = ' ';
  433.             memcpy(Line1+4, fName+1, *fName);
  434.             rep_Append(Report, Line1, true, true);
  435.         };
  436.     } else {
  437.         folderList = lastEl = nil;
  438.         if (CurScanKind == fileScan) {
  439.             wdBlock.ioNamePtr = nil;
  440.             wdBlock.ioVRefNum = fVRefNum;
  441.             wdBlock.ioWDIndex = 0;
  442.             wdBlock.ioWDProcID = 0;
  443.             wdBlock.ioWDVRefNum = 0;
  444.             (void) PBGetWDInfo(&wdBlock, false);
  445.             curDir = wdBlock.ioWDDirID;
  446.             newEl = (scn_FListElHandle)NewHandle(sizeof(scn_FListEl));
  447.             (**newEl).next = nil;
  448.             utl_CopyPString((**newEl).name, fName);
  449.             folderList = lastEl = newEl;
  450.         } else if (CurScanKind == foldScan) { 
  451.             curDir = dirID;
  452.         } else {
  453.             curDir = fsRtDirID;
  454.         };
  455.         firstDir = true;
  456.         while (true) {
  457.             dBlock.dirInfo.ioNamePtr = &dirName;
  458.             dBlock.dirInfo.ioVRefNum = volRefNum;
  459.             dBlock.dirInfo.ioFDirIndex = -1;
  460.             dBlock.dirInfo.ioDrDirID = curDir;
  461.             /* dBlock.dirInfo.ioACUser = 0; */
  462.             *(&dBlock.dirInfo.ioFlAttrib+1) = 0;
  463.             (void) PBGetCatInfo((CInfoPBPtr)&dBlock, false);
  464.             if (firstDir) {
  465.                 /* accessRights = dBlock.dirInfo.ioACUser; */
  466.                 accessRights = *(&dBlock.dirInfo.ioFlAttrib+1);
  467.                 firstDir = false;
  468.             };
  469.             newEl = (scn_FListElHandle)NewHandle(sizeof(scn_FListEl));
  470.             (**newEl).next = nil;
  471.             utl_CopyPString((**newEl).name, dirName);
  472.             if (folderList) {
  473.                 (**lastEl).next = newEl;
  474.                 lastEl = newEl;
  475.             } else {
  476.                 folderList = lastEl = newEl;
  477.             };
  478.             if (curDir == fsRtDirID) break;
  479.             curDir = dBlock.dirInfo.ioDrParID;
  480.         };
  481.         newEl = (scn_FListElHandle)NewHandle(sizeof(scn_FListEl));
  482.         (**newEl).next = nil;
  483.         *(**newEl).name = 0;
  484.         if (lastEl) {
  485.             (**lastEl).next = newEl;
  486.         } else {
  487.             folderList = newEl;
  488.         };
  489.         PrintPath(folderList, &level, 0);
  490.         while (folderList) {
  491.             newEl = (**folderList).next;
  492.             DisposHandle((Handle)folderList);
  493.             folderList = newEl;
  494.         };
  495.         if (CurScanKind != fileScan) 
  496.             vmsg_CheckAccess(accessRights, nil, nil);
  497.     };
  498.     
  499.     /* Write the final header messages. */
  500.     
  501.     if (CurScanKind == foldScan) {
  502.         vmsg_M0(disinfect ? fdStartStr : fsStartStr);
  503.     } else if (CurScanKind == fileScan) {
  504.         vmsg_M0(disinfect ? xdStartStr : xsStartStr);
  505.     } else {
  506.         vmsg_M0(disinfect ? ddStartStr : dsStartStr);
  507.     }
  508.     GetDateTime(&secs);
  509.     IUDateString(secs, shortDate, &nowDate);
  510.     IUTimeString(secs, true, &nowTime);
  511.     vmsg_M2(scanDateStr, &nowDate, &nowTime);
  512.     vmsg_Blank();
  513. }
  514.  
  515. /*______________________________________________________________________
  516.  
  517.     vmsg_End - End a Scan.
  518.     
  519.     Entry:        disinfect = true if disinfection run.
  520.                     canceled = true if scan canceled.
  521.                     
  522.     Exit:            *infected = true if infected file found.
  523.                     *sysInfected = true if infected file found in currently
  524.                         active system folder.
  525. _____________________________________________________________________*/
  526.  
  527.  
  528. void vmsg_End (Boolean disinfect, 
  529.     Boolean canceled, Boolean *infected, Boolean *sysInfected)
  530.  
  531. {
  532.     unsigned long    secs;                    /* current date/time */
  533.     Str255            date;                    /* date */
  534.     Str255            time;                    /* time */
  535.     
  536.     /* Set the infected and sysinfected flag. */
  537.     
  538.     *infected = TotInfected > 0;
  539.     *sysInfected = SysInfected;
  540.  
  541.     /* If scan canceled write canceled message. */
  542.     
  543.     vmsg_Blank();
  544.     if (canceled) {
  545.         vmsg_Blank();
  546.         vmsg_M0(cancelStr);
  547.         return;
  548.     };
  549.  
  550.     /* Write the "scan complete" report lines. */
  551.     
  552.     if (TotInfected || TotErrors) {
  553.         rep_Append(Report, "\p--------------------------------------------", 
  554.             true, true);
  555.     };
  556.     if (CurScanKind == foldScan) {
  557.         vmsg_M0(disinfect ? fdEndStr : fsEndStr);
  558.     } else if (CurScanKind == fileScan) {
  559.         vmsg_M0(disinfect ? xdEndStr : xsEndStr);
  560.     } else {
  561.         vmsg_M0(disinfect ? ddEndStr : dsEndStr);
  562.     }
  563.     GetDateTime(&secs);
  564.     IUDateString(secs, shortDate, &date);
  565.     IUTimeString(secs, true, &time);
  566.     vmsg_M2(scanDateStr, &date, &time);
  567.     
  568.     /* Write the summary report lines. */
  569.     
  570.     if (TotInfected || TotErrors) {
  571.         vmsg_Blank();
  572.         vmsg_M0(summaryStr);
  573.         vmsg_Blank();
  574.         PrintSum(TotFiles, totFilesStr, nil);
  575.         PrintSum(TotErrors, totErrorsStr, nil);
  576.         PrintSum(TotInfected, totInfectedStr, nil);
  577.         if (TotNoAccess) {
  578.             vmsg_Blank();
  579.             vmsg_M0(noPrivs13);
  580.             vmsg_Continue(noPrivs14, noPrivs13);
  581.             vmsg_Continue(noPrivs15, noPrivs13);
  582.         };
  583.     } else {
  584.         if (CurScanKind == fileScan) {
  585.             vmsg_Blank();
  586.             vmsg_M0(allOKFileStr);
  587.         } else { 
  588.             PrintSum(TotFiles, totFilesStr, nil);
  589.             vmsg_Blank();
  590.             if (CurScanKind == foldScan) {
  591.                 vmsg_M0(allOKFoldStr);
  592.             } else {
  593.                 vmsg_M0(allOKVolStr);
  594.             };
  595.         };
  596.     };
  597.     
  598.     /* Write the earliest infected file report lines. */
  599.     
  600.     if (TotInfected && CurScanKind != fileScan) {
  601.         vmsg_Blank();
  602.         vmsg_M1(earInfectedStr, EarliestName);
  603.         IUDateString(EarliestDate, shortDate, &date);
  604.         IUTimeString(EarliestDate, true, &time);
  605.         vmsg_M2(lastModStr, &date, &time);
  606.     };
  607.     
  608.     /* If any error messages were issued refer the user to the
  609.         document for more details. */
  610.         
  611.     if (TotErrors) {
  612.         vmsg_Blank();
  613.         vmsg_M0(errNoteStr1);
  614.         vmsg_Continue(errNoteStr2, errNoteStr1);
  615.         vmsg_Continue(errNoteStr3, errNoteStr1);
  616.     };
  617. }
  618.  
  619. /*______________________________________________________________________
  620.  
  621.     vmsg_BumpCounter - Increment a counter.
  622.     
  623.     Entry:        counter = 0 to increment number of files scanned.
  624.                     counter = 1 to increment number of infected files.
  625.                     counter = 2 to increment number of errors.
  626. _____________________________________________________________________*/
  627.  
  628.  
  629. void vmsg_BumpCounter (short counter)
  630.  
  631. {
  632.     long                x;                /* counter value to be redrawn */
  633.     short                h;                /* horizontal coord of counter */
  634.     short                v;                /* vertical coord of counter */
  635.     Rect                erase;        /* rectangle to be erased */
  636.     Str255            str;            /* string to be drawn */
  637.  
  638.     switch (counter) {
  639.         case 0:
  640.             NumScanned++;
  641.             x = NumScanned;
  642.             break;
  643.         case 1:
  644.             NumInfected++;
  645.             x = NumInfected;
  646.             break;
  647.         case 2:
  648.             NumErrors++;
  649.             x = NumErrors;
  650.             break;
  651.     };
  652.     v = CounterTop + 12*(counter+1);
  653.     h = CounterRight + 5;
  654.     SetRect(&erase, h, v-10, h+50, v+2);
  655.     EraseRect(&erase);
  656.     MoveTo(h, v);
  657.     NumToString(x, str);
  658.     DrawString(str);
  659. }
  660.  
  661. /*______________________________________________________________________
  662.  
  663.     vmsg_ClearCounters - Clear counters.
  664.     
  665.     Entry:        counterTop = top coord of main window counters.
  666.                     counterRight = right coord of main window counters.
  667. _____________________________________________________________________*/
  668.  
  669.  
  670. void vmsg_ClearCounters (short counterTop, short counterRight)
  671.  
  672. {
  673.     Rect            inval;            /* rectangle to be invalidated */
  674.  
  675.     NumScanned = NumInfected = NumErrors = 0;
  676.     SetRect(&inval, counterRight+5, counterTop, counterRight+55, counterTop+36);
  677.     InvalRect(&inval);
  678. }
  679.  
  680.